import java.util.concurrent.Semaphore;
import java.util.LinkedList;

public class SemaphoreEx {
    public static final int BUFLEN=100;
    private Buffer buf;
    private Semaphore fullspots, mutex, emptyspots;

	public class Buffer{
		private LinkedList<Integer> buf;
		private int items;
		private int size;

		public Buffer(int n){
		    buf = new LinkedList<Integer>();
		    items = 0;
		    size = n;
		}

		public Object remove(){
		    Object result = buf.getFirst();
		    buf.remove(0);
		    items--;
		    return result;
		}
		public void add(Integer item){
		    buf.add(item);
		    items++;
		}
	}

	public class Producer implements Runnable {	
		private int itemsToAdd;	

		public Producer(int n){
			itemsToAdd = n;
		}

		public void run(){
			int i = 0;
			while (true){
				try{
					Thread.sleep((int)(Math.random() * 10) * 100);
					emptyspots.acquire();
					mutex.acquire();
					buf.add(new Integer(i));
					System.out.println("Producer added " + i);
					i++;
					mutex.release();
					fullspots.release();
				} catch (InterruptedException e){
				}
			}
		}
    }

	public class Consumer extends Thread {
		private int id;
		public Consumer(int i) {
			id = i;
		}
		public void run() {
			while(true){
                try {
					fullspots.acquire();
					mutex.acquire();
					Integer item = (Integer)(buf.remove());
		            System.out.println("Consumer " + id + " removed " + item);
		            mutex.release();
		            emptyspots.release();
					Thread.sleep((int)(Math.random() * 10) * 100);
				} catch (InterruptedException e){
				}
			}
		}
	}
	public SemaphoreEx(int items, int consumers){
		buf = new Buffer(BUFLEN);
		emptyspots = new Semaphore(BUFLEN);
		mutex = new Semaphore(1);
		fullspots = new Semaphore(0);
	
		for (int id = 0; id < consumers; id++){
			Thread t = new Consumer(id);
			t.start();
		}

		(new Thread(new Producer(items))).start();
    }

    public static void main(String[] args){

		int consumers = 1;

		new SemaphoreEx(0, consumers);
    }
}
